<!DOCTYPE HTML>
<html>
<head>
    <base href="localhost://localhost:8080/">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>WebSocket</title>
</head>

<body>

<div style="text-align: center">
    <h3>WebSocket原生实现</h3>
    <input id="text" cols="50" rows="5"/>
    <button onclick="connect()">登录</button>
    <button onclick="closeWebSocket()">登出</button>
    <br>
    <label title="消息内容" for="text2"></label><input id="text2" cols="50" rows="5"/>
    <button onclick="push()">推送</button>
    <button onclick="broadcast()">广播</button>

    <br><br>
    <textarea id="message" readonly cols="50" rows="20"></textarea>
    <br>
    <button onclick="document.getElementById('message').value=''">清空内容</button>
</div>

</body>

<script type="text/javascript">
    let websocket = null;
    let token = null;
    //判断当前浏览器是否支持WebSocketindex.html
    function connect() {
        token = document.getElementById('text').value;
        if(token === '') {
            alert("请输入token");
            return;
        }
        if ('WebSocket' in window) {
            websocket = new WebSocket("ws://localhost:9000/ws/websocket/?token=" + token);

            //连接发生错误的回调方法
            websocket.onerror = function (event) {
                setMessageInnerHTML(event.data);
                reconnect();
            };

            //连接成功建立的回调方法
            websocket.onopen = function (event) {
                heartbeat.check();
                setMessageInnerHTML("【" + token + "】登陆成功，建立连接");
            };

            //接收到消息的回调方法
            websocket.onmessage = function (event) {
                heartbeat.check();
                setMessageInnerHTML(event.data);
            };

            //连接关闭的回调方法
            websocket.onclose = function () {
                setMessageInnerHTML("【" + token + "】退出成功，关闭连接");
            };

            //监听窗口关闭事件，当窗口关闭时，主动去关闭websocket连接，防止连接还没断开就关闭窗口，server端会抛异常。
            window.onbeforeunload = function () {
                closeWebSocket();
            };
        } else {
            alert("当前浏览器不支持WebSocket")
        }
    }

    //关闭连接
    function closeWebSocket() {
        websocket.close();
    }

    //重连
    let reconnectTimeoutObj;
    let lockReconnect = false;
    function reconnect() {
        if (lockReconnect) {
            return;
        }
        lockReconnect = true;
        //没连接上会一直重连，设置延迟避免请求过多
        reconnectTimeoutObj && clearTimeout(reconnectTimeoutObj);
        reconnectTimeoutObj = setTimeout(function () {
            connect();
            lockReconnect = false;
        }, 5000);
    }

    //心跳检测，10秒重置一次
    let heartbeat = {
        //心跳间隔，10s
        timeout: 10000,
        //心跳倒计时
        timeoutObj: null,
        //服务端返回心跳倒计时
        serverTimeoutObj: null,
        check: function(){
            let self = this;
            this.timeoutObj && clearTimeout(this.timeoutObj);
            this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
            this.timeoutObj = setTimeout(function(){
                //这里发送一个心跳，后端收到后，返回一个心跳消息，
                websocket.send("ping");
                setMessageInnerHTML("ping");
                self.serverTimeoutObj = setTimeout(function() {
                    setMessageInnerHTML("【" + token + "】心跳超时");
                    closeWebSocket();
                }, self.timeout);

            }, this.timeout)
        }
    };

    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').value += innerHTML + '\n';
    }

    //消息推送
    function push() {
        let httpRequest = new XMLHttpRequest();
        let msg = document.getElementById('text2').value;
        httpRequest.open("GET", "http://localhost:9000/demo-ws/push/" + token + "?message=" + msg, true);
        httpRequest.send();
    }

    //消息广播
    function broadcast() {
        let httpRequest = new XMLHttpRequest();
        let msg = document.getElementById('text2').value;
        httpRequest.open("GET", "http://localhost:9000/demo-ws/broadcast?message="+ msg, true);
        httpRequest.send();
    }
</script>
</html>